// ==UserScript==
// @name           Waze
// @namespace      waze-papyrus-extended-tools
// @include        https://world.waze.com/cartouche/*
// @include        https://www.waze.com/cartouche/*
// @match          https://world.waze.com/cartouche/*
// @match          https://www.waze.com/cartouche/*
// @version			1.2.3
// ==/UserScript==



var version = "1.2.3";

// Compatibility with google chrome web browser
// Thanks to bgodette and Tim1987
if(window.navigator.vendor.match(/Google/)) {
  console.log("Patching google chrome environment");

  unsafeWindow = (function() {
    var el = document.createElement('p');
    el.setAttribute('onclick', 'return window;');
    return el.onclick();
  })();

  GM_deleteValue = function(name) {
    localStorage.removeItem(name);
  };

  GM_getValue = function(name, defaultValue) {
    var value = localStorage.getItem(name);
    if (!value) return defaultValue;
    var type = value[0];
    value = value.substring(1);
    switch (type) {
      case 'b': return value == 'true';
      case 'n': return Number(value);
      default: return value;
    }
  };

  GM_setValue = function(name, value) {
    localStorage.setItem(name, (typeof value)[0] + value);
  };

};

function insertHead(elt, attributes) {
  var elt = document.createElement(elt);
  for(aName in attributes) elt[aName] = attributes[aName];
  document.getElementsByTagName("head")[0].appendChild(elt);
  return elt;
}

function include(src)    { insertHead('script', { type: 'text/javascript', src: src }); }
function includecss(src) { insertHead('link',   { type: 'text/css', rel: 'stylesheet', href: src}); }
function insertStyle(css) {
  var style = insertHead('STYLE', { type: 'text/css' });
  createTextNode(style, css);
}


// Some global useful variables
var $   = unsafeWindow.$;
var doc = unsafeWindow.document;
var Object = unsafeWindow.Object;
var wazeModel = unsafeWindow.wazeModel;
var Waze = unsafeWindow.Waze;
Array = unsafeWindow.Array; // Because unsafeWindow.Array have some extra prototype (like first())


var JSON;if(!JSON){JSON={};}(function(){"use strict";function f(n){return n<10?'0'+n:n;}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}if(typeof rep==='function'){value=rep.call(holder,key,value);}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}v=partial.length===0?'[]':gap?'[\n'+gap+partial.join(',\n'+gap)+'\n'+mind+']':'['+partial.join(',')+']';gap=mind;return v;}if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}return str('',{'':value});};}if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}return reviver.call(holder,key,value);}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}throw new SyntaxError('JSON.parse');};}}());


// Generic DOM element creation
function createElement(p, type, className) {
  var e = doc.createElement(type);
  if(p) p.appendChild(e);
  if (className != undefined) e.className = className;
  return e;
}

function createTextNode(p, txt) {
  p.appendChild(doc.createTextNode(txt));
  return p;
}

function doResizeHeight(elt, innerElt, propagate, others) {
  var margin = innerElt.outerHeight(true) - innerElt.height();
  if(others != null)
    for(var i = 0; i < others.length; i++)
      margin += others[i].outerHeight(true);
  innerElt.height(elt.height() - margin);
  if(propagate) innerElt.resize();
}
function autoResizeHeight(elt, innerElt, propagate, others) {
  elt.resize(function() {
    doResizeHeight(elt, innerElt, propagate, others);
  });
  doResizeHeight(elt, innerElt, propagate, others);
}

include("http://waze-papyrus-extended-tools.googlecode.com/svn/trunk/dragresize_commented.js");
include("http://waze-papyrus-extended-tools.googlecode.com/svn/trunk/sortable.js");
includecss("http://waze-papyrus-extended-tools.googlecode.com/svn/trunk/sortablestylesheet.css");


var logdiv;
function Log(msg) {
  if(logdiv) {
    createTextNode(createElement(logdiv, "DIV"), msg);
  }
}

var WinManager = {
  wins : {},    // prefix : CPopup obj
  didHide : [],


  get : function (prefix){
    var t = WinManager;
    return t.wins[prefix];
  },

  add : function (prefix, pop){
    var t = WinManager;
    t.wins[prefix] = pop;
    if (unsafeWindow.cpopupWins == null)
      unsafeWindow.cpopupWins = {};
    unsafeWindow.cpopupWins[prefix] = pop;
  },

  hideAll : function (){
    var t = WinManager;
    t.didHide = [];
    for (k in t.wins){
      if (t.wins[k].isShown()){
        t.didHide.push (t.wins[k]);
        t.wins[k].show (false);
      }
    }
  },
  restoreAll : function (){
    var t = WinManager;
    for (var i=0; i<t.didHide.length; i++)
      t.didHide[i].show (true);
  },

  Delete : function (prefix){
    var t = WinManager;
    delete t.wins[prefix];
    delete unsafeWindow.cpopupWins[prefix];
  }
};


function CDragger() {

  var listPopup = [];
  var listElt = [];
  var listHandle = [];
  var drag;

  this.addPopup = function(popup) {
    var div = popup.div.get(0);
    listPopup.push(popup);
    listElt.push(div);
    listHandle.push(popup.getTitleElt());
    drag.apply(div);

    // Allays show handles
    drag.resizeHandleSet(div, true);
    window.setTimeout(function() {
      drag.ondragmove(div, true);
    }, 1);
  };

  this.focusPopup = function(popup) {
    popup.setLayer(++drag.zIndex);
  };

  function getPopup(elt) {
    var idx = $.inArray(elt, listElt);
    if(idx >= 0) return listPopup[idx];
    Log("getPopup null: " + elt);
    unsafeWindow.BAD = elt;
  }


  drag = new unsafeWindow.DragResize('dragresize', {
    isElement: function(elm) { return $.inArray(elm, listElt) >= 0; },

    isHandle:  function(elm) { return $.inArray(elm, listHandle) >= 0; },

    ondragmove: function(elt, isResize) {
        if(isResize)  {
          var popup = getPopup(elt);
          if(popup) {
            doResizeHeight(popup.div, $(popup.getInnerElt()), true,
                           [$(popup.getHeaderElt())]);
          }
        }
      },

    ondragend: function(elt, b) {
      var popup = getPopup(elt);
      if(popup && popup.onDragEnd) popup.onDragEnd();
    }
  });

  drag.resizeHandleSet = resizeHandleSet;

  // Allow editPanel to be upper the popup if it is opened
  $('#editPanel').mousedown(function() {
    if($(this).hasClass('open')) $(this).css('zIndex', ++drag.zIndex);
  });

  // When we want to close editPanel, reset zIndex to 0
  $('#editPanel .toggler').click(function() {
    if(!$(this).parent().hasClass('open')) $(this).parent().css('zIndex', 0);
  });

  function resizeHandleSet(elm, show) {
    // Either creates, shows or hides the resize handles within an element.

    // If we're showing them, and no handles have been created, create 4 new ones.
    if (!elm._handle_tr)
    {
      var delta = "-1px";
      var dicCss = {
        tl: { top: delta, left: delta,                cursor: "nw-resize"},
        tm: { top: delta, left: 0, marginLeft: delta, cursor: "n-resize", width: "100%" },
        tr: { top: delta, right: delta,               cursor: "ne-resize" },
        ml: { top: 0, marginTop: delta, left: delta,  cursor: "w-resize", height: "100%" },
        mr: { top: 0, marginTop: delta, right: delta, cursor: "e-resize", height: "100%" },
        bl: { bottom: delta, left: delta,             cursor: "sw-resize" },
        bm: { bottom: delta, left: 0, marginLeft: delta, cursor: "s-resize", width: "100%"  },
        br: { bottom: delta, right: delta,            cursor: "se-resize" }};

      for (var h = 0; h < this.handles.length; h++)
      {
        // Create 4 news divs, assign each a generic + specific class.
        var hDiv = document.createElement('div');
        hDiv.className = this.myName + ' ' +  this.myName + '-' + this.handles[h];
        $(hDiv).css({
          position: "absolute",
          width: "0px",
          height: "0px",
          fontSize: "1px",
          background: "#EEE",
          border: "1px solid #333"
        });
       $(hDiv).css(dicCss[this.handles[h]]);

       elm['_handle_' + this.handles[h]] = elm.appendChild(hDiv);
      }
    }

    // We now have handles. Find them all and show/hide.
    for (var h = 0; h < this.handles.length; h++)
    {
      show = true;
      elm['_handle_' + this.handles[h]].style.visibility = show ? 'inherit' : 'hidden';
    }
  }
}

var dragger;


function CPopup (prefix, title, x, y, width, height, onDragEnd, onClose) {
  var pop = WinManager.get(prefix);
  if (pop){
    pop.show (false);
    return pop;
  }
  WinManager.add(prefix, this);

  this.BASE_ZINDEX = 0;

  // protos ...
  this.show = show;
  this.toggleHide = toggleHide;
  this.getTopDiv = getTopDiv;
  this.getMainDiv = getMainDiv;
  this.getLayer = getLayer;
  this.setLayer = setLayer;
  this.getLocation = getLocation;
  this.setLocation = setLocation;
  this.focusMe = focusMe;
  this.isShown = isShown;
  this.unfocusMe = unfocusMe;
  this.centerMe = centerMe;
  this.destroy = destroy;
  this.autoHeight = autoHeight;
  this.getDimension = getDimension;
  this.setDimension = setDimension;

  // object vars ...
  this.prefix = prefix;
  this.onDragEnd = onDragEnd;
  this.onClose = onClose;

  var titleElt;
  var tabHeader;
  var innerElt;
  this.getInnerElt = function() { return innerElt; };
  this.getHeaderElt = function() { return tabHeader; };
  this.getTitleElt = function() { return titleElt; };

  this.div = $(createElement(doc.body, 'DIV', 'CPopup_' + prefix));
  this.div.css({
    backgroundColor: "#f8f8f8",
    padding: 6,
    border: "3px double",
    ridge: "#666",

    background: "#fff",
    zIndex: this.BASE_ZINDEX,
    display: 'none',
    left: x,
    top: y,
    width: width,
    height: height,
    position: 'absolute'
  });


  tabHeader = createElement(this.div.get(0), 'TABLE');
  $(tabHeader).css({ marginBottom: 10 });

  var tr = createElement(tabHeader, 'TR');
  var td = createElement(tr, 'TD', 'CPopup_' + prefix);
  $(td).css({
    width: '100%',
    backgroundColor: 'lightBlue',
    fontWeight: 'bold'
  });
  titleElt = td;
  createTextNode(titleElt, title);


  var td = createElement(tr, 'TD');
  $(td).css({
    backgroundColor: 'lightBlue',
    paddingLeft: 5,
    paddingRight: 5
  });

  var transparenceVal = [30, 70, 100];
  for(var i in transparenceVal) {
    var tv = transparenceVal[i];
    var but = createElement(td, 'INPUT');
    but.type = "button";
    but.value = tv;
    $(but).css({ fontSize: 8, borderWidth: 0, backgroundColor: "transparent" });
    but.addEventListener("click", function(div, tv) {
      return function(e) { div.css({ opacity: tv/100 }); };
    }(this.div, tv));
  }

  var td = $(createElement(tr, 'TD'));
  td.css({
    textAlign: 'middle',
    verticalAlign: 'middle',
    color: "#fff",
    background: "#333",
    fontWeight: "bold",
    fontSize: "14px",
    padding: "0px 5px" });
  var xElt = td;

  createTextNode(xElt.get(0), 'X');
  xElt.mouseover(function() { $(this).css("cursor", "pointer"); });
  xElt.click({ me: this }, function(ev) { var me = ev.data.me; me.show(false); if (me.onClose != null) me.onClose(); });

  innerElt = createElement(this.div.get(0), 'DIV', "innerElt");

  dragger.addPopup(this);

  function autoHeight(onoff) {
    this.div.css('height', onoff ? '' : this.div.css('maxHeight'));
  }
  function focusMe() {
    dragger.focusPopup(this);
    for (k in unsafeWindow.cpopupWins) {
      if (k != this.prefix)
        unsafeWindow.cpopupWins[k].unfocusMe();
    }
  }
  function unfocusMe() {
//    this.setLayer(-5);
  }
  function getLocation() {
    return {x: parseInt(this.div.css('left')), y: parseInt(this.div.css('top'))};
  }
  function setLocation(loc) {
    this.div.css({left: loc.x + 'px', top: loc.y + 'px'});
  }
  function destroy() {
    document.body.removeChild(this.div.get(0));
    WinManager.Delete (this.prefix);
  }
  function centerMe (parent) {
    if (parent == null) {
      var coords = getClientCoords(document.body);
    } else
      var coords = getClientCoords(parent);
    var x = ((coords.width - parseInt(this.div.css('width'))) / 2) + coords.x;
    var y = ((coords.height - parseInt(this.div.css('height'))) / 2) + coords.y;
    if (x<0) x = 0;
    if (y<0) y = 0;
    this.div.css({left: x +'px', top: y +'px'});
  }
  function setLayer(zi) {
    this.div.css('zIndex', this.BASE_ZINDEX + zi);
  }
  function getLayer() {
    return parseInt(this.div.css('zIndex')) - this.BASE_ZINDEX;
  }
  function getTopDiv() {
    return document.getElementById(this.prefix+'_top');
  }
  function getMainDiv() {
    return document.getElementById(this.prefix+'_main');
  }
  function isShown() {
    return this.div.css('display') == 'block';
  }
  function show(tf) {
    if (tf) {
      this.div.css('display', 'block');
      this.focusMe();
    } else {
      this.div.css('display', 'none');
    }
  }
  function toggleHide() {
    this.show(this.isShown() == false);
  }

  function getDimension() {
    with(this.div) {
      var p = position();
      p['width'] = width();
      p['height'] = height();
      return p;
    }
  }

  function setDimension(p) {
    this.div.css(p);
  }

}


// Main code for waze tools


function readOptions() {
  var s = GM_getValue("Options", '{}');
  var opt = JSON.parse(s);
  return opt;
}

function writeOptions(opt) {
  var s = JSON.stringify(opt);
  GM_setValue("Options", s);
}


function returnIt(it) { return function() { return it; }; }

function CSegment(seg) {

  // this.seg = seg;
  this.getSegment = returnIt(seg);

  this.getStreet = function() {
    var street;
    var sid = seg.attributes.primaryStreetID;
    if(sid) street = wazeModel.streets.objects[sid];
    this.getStreet = returnIt(street);
    // this.street = street;
    return street;
  };

  this.getCity = function() {
    var city;
    var street = this.getStreet();
    var cityId = street ? street.cityID : null;
    if(cityId) city = wazeModel.cities.objects[cityId];
    this.getCity = returnIt(city);
    // this.city = city;
    return city;
  };

  this.getState = function() {
    var state;
    var city = this.getCity();
    var stateId = city ? city.stateID : null;
    if (stateId) state = wazeModel.states.objects[stateId];
    this.getState = returnIt(state);
    // this.state = state;
    return state;
  };

  this.getCountry = function() {
    var country;
    var city = this.getCity();
    var countryId = city ? city.countryID : null;
    if(countryId) country = wazeModel.countries.objects[countryId];
    this.getCountry = returnIt(country);
    return country;
  };

  this.getStreetSplited = function() {
     var name = this.getStreet().name;
     if(name == null) return [];
     var t = name.split(" - ");
     this.getStreetSplited = returnIt(t);
     return t;
  }

  this.getStreetPart = function(i) {
    var t = this.getStreetSplited();
    if(i < t.length) return t[i];
    return "";
  };

  this.getStreetIdOrNamePart = function(y) {
    var t = this.getStreetSplited();
    var r = /^[^ ]*[0-9][^ ]*$/;
    var l = [];
    for(var i = 0; i < t.length; i++) {
      var n = t[i];
      if((n.match(r)!=null) == y) l.push(n);
    }
    return l.join(" - ");
  };

  this.getStreetIdPart = function() {
    var r = this.getStreetIdOrNamePart(true);
    this.getStreetIdPart = returnIt(r);
    return r;
  }

  this.getStreetNamePart = function() {
    var r = this.getStreetIdOrNamePart(false);
    this.getStreetNamePart = returnIt(r);
    return r;
  }
}

function convertSegList2Waze(segList) {
  var lSeg = new Array();
  for(var i = 0; i < segList.length; i++)
    lSeg.push(segList[i].getSegment());
  return lSeg;
}

function convertListWazeSeg(segList) {
  var lSeg = [];
  
  function add(elt) {
    if(elt.type == Waze.Model.ObjectType.SEGMENT) lSeg.push(new CSegment(elt));
  }
  
  if(segList instanceof Array)
    for(var i = 0; i < segList.length; i++) add(segList[i]);
  else
    for(var i in segList) add(segList[i]);

    return lSeg;
}

var Columns = {
  Country: function() {
    this.getName = function() { return "Ctry"; };
    this.getValue = function(seg) {
      var countryName = seg.getCountry().name;
      if(countryName == "Belgium") countryName = "BE";
      if(countryName == "Canada") countryName = "CAN";
      if(countryName == "Finland") countryName = "FI";
      if(countryName == "France") countryName = "F";
      if(countryName == "Germany") countryName = "DE";
      if(countryName == "Mexico") countryName = "MEX";
      if(countryName == "Netherlands") countryName = "NL";
      if(countryName == "United Kingdom") countryName = "UK";
      if(countryName == "United States") countryName = "USA";
      this.getValue = returnIt(countryName);
      return countryName;
    };
  },

  State: function() {
    this.getName = function() { return "State"; };
    this.getValue = function(seg) { return seg.getState() ? seg.getState().name : ""; };
  },

  City: function() {
    this.getName = function() { return "City"; };
    this.getValue = function(seg) { return seg.getCity() ? seg.getCity().name : ""; };
  },

  Street: function() {
    this.getName = function() { return "Street"; };
    this.getValue = function(seg) { return seg.getStreet() ? seg.getStreet().name : ""; };
  },

  StreetId: function() {
    this.getName = function() { return "Street id"; };
    this.getValue = function(seg) { return seg.getStreetIdPart() ? seg.getStreetIdPart() : ""; };
  },

  StreetName: function() {
    this.getName = function() { return "Street name"; };
    this.getValue = function(seg) { return seg.getStreetNamePart() ? seg.getStreetNamePart() : ""; };
  },

  Locked: function() {
    this.getName = function() { return "Locked"; };
    this.getValue = function(seg) { return seg.getSegment().attributes.locked; };
  },

  Separator: function() {
    this.getName = function() { return "Sep"; };
    this.getValue = function(seg) { return seg.getSegment().attributes.separator; };
  },

  Level: function() {
    this.getName = function() { return "Level"; };
    this.getValue = function(seg) { return seg.getSegment().attributes.level; };
  },

  Length: function() {
    this.getName = function() { return "Length"; };
  //  this.getValue = function(seg) { return seg.getSegment().attributes.length; };
    this.createAction = function(td, segCol) {
      var l = 0;
      for(var i = 0; i < segCol.length; i++) {
        var seg = segCol[i];
        l += seg.getSegment().attributes.length;
      }
      createTextNode(td, l);
    };
  },

  RoadType: function() {
    this.getName = function() { return "Type"; };
    this.getValue = function(seg) {
		var TypVal = seg.getSegment().attributes.roadType; ;
		var TypTab = new Array();
			TypTab[1] = "Streets";
			TypTab[2] = "Primary Street";
			TypTab[3] = "Freeways";
			TypTab[4] = "Ramps";
			TypTab[5] = "Walking Trails";
			TypTab[6] = "Major Highway";
			TypTab[7] = "Minor Highway";
			TypTab[8] = "Dirt roads";
			TypTab[10] = "Pedestrian Bw";
			TypTab[16] = "Stairway";
			TypTab[17] = "Private Road";
			TypTab[18] = "Railroad";
			TypTab[19] = "Runway/Taxiway";
			TypTab[20] = "Parking Lot Road";
			TypTab[21] = "Service Road";
		return TypTab[TypVal] ;
	};
  },

  NbWays: function() {
    this.getName = function() { return "Nb ways"; };
    this.getValue = function(seg) {
      var attr = seg.getSegment().attributes;
      return (attr.fwdDirection?1:0) + (attr.revDirection?1:0);
    };
  },

  RoundAbout: function() {
    this.getName = function() { return "is RndAbt"; };
    this.getValue = function(seg) {
      return seg.getSegment().attributes.junctionID!=null;
    };
  },

  AllowNoDirection: function() {
    this.getName = function() { return "allowNoDirection"; };
    this.getValue = function(seg) {
      return seg.getSegment().attributes.allowNoDirection;
    };
  },

  FwdSpeed: function() {
    this.getName = function() { return "Fwd speed"; };
    this.getValue = function(seg) {
      return seg.getSegment().attributes.fwdCrossSpeed;
    };
  },

  RevSpeed: function() {
    this.getName = function() { return "Rev speed"; };
    this.getValue = function(seg) {
      return seg.getSegment().attributes.revCrossSpeed;
    };
  },

  Select: function() {
    this.getName = function() { return "Select"; };

    this.createAction = function(td, segCol) {
      var but = createElement(td, "INPUT");
      but.type = "button";
      but.value = segCol.length;
      but.addEventListener("click", function(e) {
        unsafeWindow.selectionManager.select(convertSegList2Waze(segCol));
      });
    };
  }
};

with(Columns) {
  Columns.Available = [
      Country, State, City, Street, StreetId, StreetName,
      Length, Locked, Separator, Level, RoadType,
      NbWays, RoundAbout, AllowNoDirection, FwdSpeed, RevSpeed,
      Select];
};


function CPopupListSeg() {

  var segPopup;
  var segCol;
  var columns = [];

  this.tab;

  this.show = function() {
    this.redraw();
    segPopup.show(true);
  };

  this.addColumn = function(col) { columns.push(col); };

  this.getColumns = function() { return columns; };

  this.initialize = function() {
    segPopup = new CPopup("segPopup", "Segment list", 1, 1, 2, 2, function() {
      Options.segPopupPos = segPopup.getDimension();
      writeOptions(Options);
      console.log(JSON.stringify(Options));
    });
    segPopup.setDimension(Options.segPopupPos);
    segPopup.show(false);

    var butSpan = createElement(segPopup.getInnerElt(), "SPAN");

    var but = createElement(butSpan, "INPUT");
    but.type = "button";
    but.value = "Choose columns";
    but.addEventListener("click", function(t) {
        return function(e) { new CPopupColChooser(t); };
    }(this));

    var but = createElement(butSpan, "INPUT");
    but.type = "button";
    but.value = "All";
    but.addEventListener("click", function(t) {
      return function(e) { segCol = null; t.redraw(); };
    }(this));

    var but = createElement(butSpan, "INPUT");
    but.type = "button";
    but.value = "Selection";
    but.addEventListener("click", function(t) {
      return function(e) { segCol = convertListWazeSeg(unsafeWindow.selectionManager.selectedItems); t.redraw(); };
    }(this));

    var scrol = createElement(segPopup.getInnerElt(), "DIV");
    this.tab = createElement(scrol, "TABLE", "myTable");

    scrol = $(scrol);
    scrol.css({ height: "100%", overflow: "auto" });

    autoResizeHeight($(segPopup.getInnerElt()), scrol, false, [$(butSpan)]);
  };

  function getSegDic() {
    segDic = {};
    for(var i = 0; i < segCol.length; i++) {
      var seg = segCol[i];
      var curDic = segDic;
      for(var j = 0; j < columns.length; j++) {
        var col = columns[j];
        if(col.getValue) {
          var val = col.getValue(seg);
          var dicVal = curDic[val];
          if(dicVal == null) { dicVal = {}; curDic[val] = dicVal; }
          curDic = dicVal;
        }
      }
      var lSeg = curDic[null];
      if(lSeg == null) { lSeg = new Array(); curDic[null] = lSeg; }
      lSeg.push(seg);
    }
    return segDic;
  }

  function addRow(tbody, segCol) {
    if(segCol instanceof Array) {
      if(segCol.length > 0) {
        var seg = segCol[0];
        var tr = createElement(tbody, "TR");
        for(var j = 0; j < columns.length; j++) {
          var col = columns[j];
          var td = createElement(tr, "TD");
          if(col.getValue)
            createTextNode(td, col.getValue(seg));
          if(col.createAction)
            col.createAction(td, segCol);
        }
      }
    }
    else {
      for(var colIdx in segCol) {
        addRow(tbody, segCol[colIdx]);
      }
    }
  }

  this.redraw = function() {
    $(this.tab).empty();
    
    if(segCol == null)
      segCol = convertListWazeSeg(wazeModel.segments.objects);

    var thead = createElement(this.tab, "THEAD");

    var tr = createElement(thead, "TR");
    for(var i = 0; i < columns.length; i++) {
      var col = columns[i];
      createTextNode(createElement(tr, "TD"), col.getName());
    }
    $(thead).css({fontSize: "12px", fontWeight: "bold"});

    var tbody = createElement(this.tab, "TBODY");
    var segDic = getSegDic();
    addRow(tbody, segDic);

    unsafeWindow.ts_makeSortable(this.tab);
  };

  this.addColumn(new Columns.Country());
  this.addColumn(new Columns.State());
  this.addColumn(new Columns.City());
  this.addColumn(new Columns.Street());
  this.addColumn(new Columns.Select());
  this.addColumn(new Columns.RoadType());
  this.addColumn(new Columns.Level());
  this.addColumn(new Columns.RoundAbout());
  this.addColumn(new Columns.Locked());
  this.initialize();
}


function CPopupColChooser(segTable) {
  var popup = new CPopup("columnChooser", "Choose displayed columns", 1, 1, 2, 2, function() {
    Options.segColChooserPos = popup.getDimension();
    writeOptions(Options);
    console.log(JSON.stringify(Options));
  });
  popup.setDimension(Options.segColChooserPos);
  popup.show(true);

  var checkedColumns = [];
  var currentColumns = segTable.getColumns();

  function addDisplayColumn(col, tab, enabled) {
    var tr = createElement(tab, "TR");
    var input = createElement(createElement(tr, "TD"), "INPUT");
    input.type = "checkbox";
    if(enabled) {
      input.checked = "checked";
      checkedColumns.push(col);
    }
    createTextNode(createElement(tr, "TD"), new col().getName());

    $(input).change({col: col}, function(data) {
     if($(this).attr('checked'))
       checkedColumns.push(col);
     else {
       checkedColumns.splice($.inArray(col, checkedColumns), 1);
     }
     setNewList();
    });

  }

  function setNewList() {
    currentColumns.splice(0, currentColumns.length);
    for(var i = 0; i  < checkedColumns.length; i++) {
      var col = checkedColumns[i];
      currentColumns.push(new col());
    }
    segTable.redraw();
  }

  function colInCurrent(col) {
    for(var i = 0; i < currentColumns.length; i++) {
      var cur = currentColumns[i];
      if(cur instanceof col) return true;
    }
    return false;
  }

  function redraw() {
    $(popup.getInnerElt()).empty();

    var scrol = createElement(popup.getInnerElt(), "DIV");
    var tab = createElement(scrol, "TABLE", "myTable");
    scrol = $(scrol);
    scrol.css({ height: "100%", overflow: "auto" });

    for(var i = 0; i < Columns.Available.length; i++) {
      var col = Columns.Available[i];
      addDisplayColumn(col, tab, colInCurrent(col));
    }

    autoResizeHeight($(popup.getInnerElt()), $(scrol), false);
  }


  redraw();
}


function applySegmentEditDetails(action, seg, attr) {
  // var b = $("#editPanel .contents");
  // var k = readSegmentEditDetails(b);
  var k = attr;
  var currentEditDetails = new Waze.AttributeCollector().collectDetails(seg);
  
  var o = {};
  for (var m in k) {
    if (k[m] !== currentEditDetails[m]) {
      o[m] = k[m]
    }
  }
  delete o.id;
  if (Object.keys(o).isEmpty()) {
    return
  }
  // var g = new Waze.Action.MultiAction();
  // wazeModel.actionManager.add(g);
  var g = action;
  
  var p = {};
  if (Waze.Util.defined(o.direction)) {
    p.fwdDirection = (o.direction & 1) != 0;
    p.revDirection = (o.direction & 2) != 0
  }
  Waze.Util.copyAttributes(p, o, ["roadType", "level", "separator", "locked"]);
  if ((Waze.Util.isDefined(o.streetName) || Waze.Util.isDefined(o.countryID) || Waze.Util.isDefined(o.cityName) || Waze.Util.isDefined(o.stateID) || Waze.Util.isDefined(o.emptyCity) || Waze.Util.isDefined(o.emptyStreet)) && (k.streetName != "" || k.emptyStreet) && (k.streetName != Waze.AttributeCollector.MULTIPLE_VALEUS) && (k.cityName != "" || k.emptyCity) && (k.cityName != Waze.AttributeCollector.MULTIPLE_VALEUS) && (k.countryID != "") && (k.countryID != Waze.AttributeCollector.MULTIPLE_VALEUS) && (k.stateID != "" && (k.stateID != Waze.AttributeCollector.MULTIPLE_VALEUS))) {
    var d = wazeModel.countries.get(k.countryID);
    var a = wazeModel.states.get(k.stateID);
    var j;
    if (k.emptyCity) {
      j = wazeModel.cities.getByAttributes({
            countryID : d.getID(),
            stateID : a.getID(),
            isEmpty : true
          })
    } else {
      j = wazeModel.cities.getByAttributes({
            name : k.cityName,
            countryID : d.getID(),
            stateID : a.getID()
          })
    }
    if (j.length == 1) {
      j = j[0];
      if (o.cityEnglishName) {
        g.doSubAction(new Waze.Action.UpdateObject(j, {
              englishName : o.cityEnglishName
            }))
      }
    } else {
      if (j.length === 0) {
        Waze.Console.info("Creating new city " + (k.emptyCity ? "EMPTY" : k.cityName));
        var h = {
          englishName : k.cityEnglishName,
          countryID : d.getID(),
          stateID : a.getID()
        };
        if (k.emptyCity) {
          h.isEmpty = true;
          h.name = ""
        } else {
          h.name = k.cityName
        }
        var f = new Waze.Action.CreateObject(Waze.Model.ObjectType.CITY, h);
        g.doSubAction(f);
        j = f.newObject
      } else {
        Waze.Console.error("Multiple cities with the name " + k.cityName + " in the same country " + k.countryID);
        return
      }
    }
    var c;
    if (k.emptyStreet) {
      c = wazeModel.streets.getByAttributes({
            isEmpty : true,
            cityID : j.getID()
          })
    } else {
      c = wazeModel.streets.getByAttributes({
            name : k.streetName,
            cityID : j.getID()
          })
    }
    if (c.length == 1) {
      c = c[0]
    } else {
      if (c.length === 0) {
        Waze.Console.info("Creating new street " + (k.emptyStreet ? "EMPTY" : k.streetName));
        var e;
        if (k.emptyStreet) {
          e = new Waze.Action.CreateObject(Waze.Model.ObjectType.STREET, {
                name : "",
                isEmpty : true,
                cityID : j.getID()
              })
        } else {
          e = new Waze.Action.CreateObject(Waze.Model.ObjectType.STREET, {
                name : k.streetName,
                cityID : j.getID()
              })
        }
        g.doSubAction(e);
        c = e.newObject
      } else {
        Waze.Console.error("Multiple streets with the name " + k.streetName + " in the same city " + k.cityName);
        return
      }
    }
    p.primaryStreetID = c.getID()
  }
  // for (var l = 0, n = selectionManager.selectedItems.length; l < n; l++) {
    // g.doSubAction(new Waze.Action.UpdateSegmentDetails(selectionManager.selectedItems[l], p))
  // }
  g.doSubAction(new Waze.Action.UpdateSegmentDetails(seg, p));
}


function CEditColStreet() {
  this.getName = function() { return "Street"; };
  this.getValue = function(attr, lSeg) { return attr.streetName; };
  this.updateAttr = function(attr, txt) {
    attr.streetName = txt;
    attr.emptyStreet = (txt=="");
  }
}

function CEditColCity() {
  this.getName = function() { return "City"; };
  this.getValue = function(attr, lSeg) { return attr.cityName; };
  this.updateAttr = function(attr, txt) {
    attr.cityName = txt;
    attr.emptyCity = (txt=="");
  }
}

function CEditColStreetPart(part) {
  this.getName = function() { return "Str part " + part; };
  this.getValue = function(attr, lSeg) {
    if(lSeg.length == 0) return "";
    var s = lSeg[0].getStreetPart(part);
    for(var i = 1; i < lSeg.length; i++)
      if(lSeg[i].getStreetPart(part) != s)
        return "";
    return s;
  };
  
  this.updateAttr = function(attr, txt) {
    var name = attr.streetName.split(" - ");
    name[part] = txt;
    txt = name.join(" - ");
    attr.streetName = txt;
    attr.emptyStreet = (txt=="");
  }
}

function CEditColStreetIdPart() {
  this.getName = function() { return "Str id"; };
  this.getValue = function(attr, lSeg) {
    if(lSeg.length == 0) return "";
    var s = lSeg[0].getStreetIdPart();
    for(var i = 1; i < lSeg.length; i++)
      if(lSeg[i].getStreetIdPart() != s)
        return "";
    return s;
  };
  
  this.updateAttr = function(attr, txt) {
    var seg = new CSegment(null);
    seg.getStreet = returnIt({name: attr.streetName});
    var t = [txt, seg.getStreetNamePart()];
    t = t.filter(function(e){return e != ""});
    txt = t.join(" - ");
    attr.streetName = txt;
    attr.emptyStreet = (txt=="");
  }
}

function CEditColStreetNamePart() {
  this.getName = function() { return "Str name"; };
  this.getValue = function(attr, lSeg) {
    if(lSeg.length == 0) return "";
    var s = lSeg[0].getStreetNamePart();
    for(var i = 1; i < lSeg.length; i++)
      if(lSeg[i].getStreetNamePart() != s)
        return "";
    return s;
  };
  
  this.updateAttr = function(attr, txt) {
    var seg = new CSegment(null);
    seg.getStreet = returnIt({name: attr.streetName});
    var t = [seg.getStreetIdPart(), txt];
    t = t.filter(function(e){return e != ""});
    txt = t.join(" - ");
    attr.streetName = txt;
    attr.emptyStreet = (txt=="");
  }
}

function CPopupEdit(listSeg) {
  var popup = new CPopup("Edit", "Edit segments", 1, 1, 2, 2, function() {
    Options.editPos = popup.getDimension();
    writeOptions(Options);
    console.log(JSON.stringify(Options));
  });
  popup.setDimension(Options.editPos);
  popup.show(true);
  
  var attrCollector = new Waze.AttributeCollector();
  
  var editCols = [ { controler: new CEditColCity() },
                   { controler: new CEditColStreet() },
                   // { controler: new CEditColStreetPart(0) },
                   // { controler: new CEditColStreetPart(1) },
                   // { controler: new CEditColStreetPart(2) },
                   { controler: new CEditColStreetIdPart() },
                   { controler: new CEditColStreetNamePart() },
                 ];
  
  function initGui() {
    $(popup.getInnerElt()).empty();
    $(popup.getInnerElt()).css({ height: "100%", overflow: "auto" });
    
    if(listSeg.length == 0) {
      createTextNode(popup.getInnerElt(), "No segment selected");
      return;
    }

    var attr = attrCollector.collectDetails(convertSegList2Waze(listSeg));
    
    var table = createElement(popup.getInnerElt(), "TABLE", "myTable");
    var thead = createElement(table, "THEAD");
    var tr = createElement(thead, "TR");
    createTextNode(createElement(tr, "TD"), "Attribute");
    createTextNode(createElement(tr, "TD"), "Edit");
    createTextNode(createElement(tr, "TD"), "Value");
    
    for(var i = 0; i < editCols.length; i++) {
      var editCol = editCols[i];
      var tr = createElement(table, "TR");
      createTextNode(createElement(tr, "TD"), editCol.controler.getName());
      var cb = createElement(createElement(tr, "TD"), "INPUT");
      cb.type = "checkbox";
      var txt = createElement(createElement(tr, "TD"), "INPUT");
      txt.value = editCol.controler.getValue(attr, listSeg);
      txt.disabled = "y";
      editCol.enabled = false;
      editCol.txt = txt;
      
      $(cb).change(function(editCol) { return function() {
        editCol.enabled = $(this).attr('checked');
        editCol.txt.disabled = editCol.enabled ? undefined : "y";
      }; }(editCol));
    }
    
    var but = createElement(popup.getInnerElt(), "INPUT");
    but.type = "button";
    but.value = "Apply";
    $(but).click(apply);
  }
  
  function apply() {
    var action = new Waze.Action.MultiAction();
    wazeModel.actionManager.add(action);
    for(var segId = 0; segId < listSeg.length; segId++) {
      var seg = listSeg[segId];
      var attr = attrCollector.collectDetails(seg.getSegment());
      for(var i = 0; i < editCols.length; i++) {
        var editCol = editCols[i];
        if(editCol.enabled) {
          editCol.controler.updateAttr(attr, editCol.txt.value);
        }
      }
      applySegmentEditDetails(action, seg.getSegment(), attr);
    }
  }

  initGui();
}


function CSelectLandMark() {
  var lm = [];
  
  for(var i = 0; i < unsafeWindow.selectionManager.selectedItems.length; i++) {
    var m = unsafeWindow.selectionManager.selectedItems[i];
    if(m.type == Waze.Model.ObjectType.LANDMARK) lm.push(m);
  }
  
  if(lm.length == 0) {
    for(var id in wazeModel.landmarks.objects) {
      if(id < 0) {
        var curLm = wazeModel.landmarks.objects[id];
        if(lm.length == 0 || id < lm[0].fid) lm[0] = curLm;
      }
    }
  }
  
  if(lm.length > 0) {
    var lSeg = new Array();
    for(var id in wazeModel.segments.objects) {
      var seg = wazeModel.segments.objects[id];
      var ok = true;
      for(var i = 0; i < seg.geometry.components.length && ok; i++) {
        var p = seg.geometry.components[i];
        var found = false;
        for(var j = 0; j < lm.length && !found; j++) {
          if(lm[j].geometry.containsPoint(p)) found = true;
        }
        if(!found) ok = false;
      }
      if(ok) lSeg.push(seg);
    }
    
    unsafeWindow.selectionManager.select(lSeg);
  }
  else {
    Log("No new or selected landmark found");
  }
}


function CCleanRoundAbout() {
  var dicSeg = {};
  
  var attrCollector = new Waze.AttributeCollector();

  for(var i = 0; i < unsafeWindow.selectionManager.selectedItems.length; i++) {
    var seg = unsafeWindow.selectionManager.selectedItems[i];
    if(seg.type == Waze.Model.ObjectType.SEGMENT && seg.attributes.junctionID != null) {
      var jId = seg.attributes.junctionID;
      // Log("jId = " + jId);
      var junc = wazeModel.junctions.objects[jId];
      if(junc != null) {
        var segInfo = dicSeg[jId];
        if(segInfo==null) { segInfo = {list:[], attr:{}}; dicSeg[jId] = segInfo; }
        var lSeg = segInfo.list;
        var attr = segInfo.attr;
        var segIdList = junc.segIDs;
        for(var j = 0; j < segIdList.length; j++) {
          var sid = segIdList[j];
          // Log("sid = " + sid);
          var seg = wazeModel.segments.objects[sid];
          if(seg != null) {
            lSeg.push(seg);
            
            var nodeId = seg.attributes.fromNodeID;
            // Log("nodeId = " + nodeId);
            var node = wazeModel.nodes.objects[nodeId];
            if(node != null) {
              var nodeSegIdList = node.attributes.segIDs;
              for(var k = 0; k < nodeSegIdList.length; k++) {
                var nodeSegId = nodeSegIdList[k];
                // Log("nodeSegId = " + nodeSegId);
                
                var subSeg = wazeModel.segments.objects[nodeSegId];
                if(subSeg != null) {
                  if(subSeg.attributes.junctionID==jId) continue;
                  var subSegAttr = attrCollector.collectDetails(subSeg);
                  // Log("subSegAttr = " + JSON.stringify(subSegAttr));
                  if(attr.roadType==undefined) {
                    Waze.Util.copyAttributes(attr, subSegAttr, ["roadType", "countryID", "countryName", "emptyCountry", "stateID", "stateName", "emptyState", "cityID", "cityName", "emptyCity", "streetID", "streetName", "emptyStreet"]);
                    attr.locked = true;
                  }
                  else {
                    [["countryID",null],
                     ["country", "emptyCountry"],
                     ["stateID", null],
                     ["stateName", "emptyState"],
                     ["cityID", null],
                     ["city", "emptyCity"],
                     ["streetID", null],
                     ["street", "emptyStreet"]].forEach(function(data) {
                      var name = data[0];
                      var empty = data[1];
                      if(attr[name] != subSegAttr[name]) { attr[name] = ""; if(empty!=null) attr[empty] = true; }
                    });
                    if(attr.roadType<subSegAttr.roadType) attr.roadType = subSegAttr.roadType;
                  }
                  // Log("attr = " + JSON.stringify(attr));
                }
              }
            }
          }
        }
      }
    }
  }
  
  var action = new Waze.Action.MultiAction();
  wazeModel.actionManager.add(action);
  Object.forEach(dicSeg, function(jId, segInfo) {
    segInfo.list.forEach(function(seg) {
      applySegmentEditDetails(action, seg, segInfo.attr);
    });
  });
  
  if(dicSeg.length == 1) {
    unsafeWindow.selectionManager.select(dicSeg[Object.keys(dicSeg)[0]].list);
  }
}



var defOptions = {
  segPopupPos: { left: 20, top: 220, width: 220, height: 330 },
  segColChooserPos: { left: 40, top: 240, width: 220, height: 330 },
  editPos: { left: 40, top: 240, width: 220, height: 330 }
};

var Options = {};
var o = readOptions();
console.log(JSON.stringify(o));

for(var k in defOptions) Options[k] = (typeof o[k] != 'undefined') ? o[k] : defOptions[k];
console.log(JSON.stringify(Options));

var css = [
  ".scriptVer {font-size: 10px; text-align: right }",
  ".myTable { font-size: 10px }",
  ".myTable td, .myTable th { border-width: 1px; border-style: solid; padding: 2px }",
  ".myTable thead td, .myTable th { font-size: 12px; font-weight: bold }"
];
insertStyle(css.join("\n"));


$(doc).ready(function() {
  console.log("Ready");
  window.setTimeout(function() {
    console.log("Timeout");
    $("#map-problems-explanation").remove();
    
    var user = $("#user-details");
    user.css({"padding-top": 10});
    user.children(".welcome-message").remove();
    
    with($("<p>").prependTo(user)) {
      append($("<span>").addClass("points").append(user.children(".points").children("span")).append(" pts.").css({"margin-right": 10}));
      append($("<span>").addClass("unsaved-changes").append(user.children(".unsaved-changes").children("span")).css({"margin-right": 10}));
      append($("<span>").addClass("scriptVer").append("Extended tools V" + version));
    }
    user.children(".points").remove();
    user.children(".unsaved-changes").remove();
    
    dragger = new CDragger();

    wazeModel = unsafeWindow.wazeModel;

    var segListPopup = null;

    var para = createElement(null, "P");
    user.append(para);

    var but = createElement(para, "INPUT");
    but.type = "button";
    but.value = "Show segment list";
    but.addEventListener("click", function(e) {
      if(segListPopup == null)
        segListPopup = new CPopupListSeg();
      segListPopup.show();
    });

    var but = createElement(para, "INPUT");
    but.type = "button";
    but.value = "Edit selection properties";
    but.addEventListener("click", function(e) {
      new CPopupEdit(convertListWazeSeg(unsafeWindow.selectionManager.selectedItems));
    });

    var but = createElement(para, "INPUT");
    but.type = "button";
    but.value = "Select in landmark";
    but.addEventListener("click", function(e) {
      new CSelectLandMark();
    });
    
    var but = createElement(para, "INPUT");
    but.type = "button";
    but.value = "Clean round about";
    but.addEventListener("click", function(e) {
      new CCleanRoundAbout();
    });
    
    logdiv = createElement(para, "DIV");
    $(logdiv).css("font-size", "10px");

    // var trts = unsafeWindow.Waze.Model.SegmentRepository.prototype.roadTypeScales;
    // var tnrts = {};
    // for(var i in trts) {
      // var rts = trts[i];
      // tnrts[i] = [rts[0], 100000];
    // }
    // unsafeWindow.Waze.Model.SegmentRepository.prototype.roadTypeScales = tnrts;
    // unsafeWindow.A = trts;
    // unsafeWindow.B = tnrts;

    // Allowing multi-selection on landmark
    with(unsafeWindow.selectionManager) {
      allowedSelections[typeBitmap[multiple[Waze.Model.ObjectType.LANDMARK]]] = true;
    }
  }, 5000);
});
